#include "config.h"

#include <gtest/gtest.h>

#include <limits>
#include <memory>
#include <set>
#include <string>

using namespace cfg;

// 构建一个简单的对象树：
// root: {
//   "a": 1,
//   "b": [ 10, 20, {"x": "ok"} ],
//   "s": "text"
// }
static std::shared_ptr<const Object>
make_sample()
{
    auto arr = std::make_shared<Array>();
    arr->items.push_back(int64_t{10});
    arr->items.push_back(int64_t{20});
    auto obj_in_arr = std::make_shared<Object>();
    obj_in_arr->props["x"] = std::string("ok");
    arr->items.push_back(std::shared_ptr<const Object>(obj_in_arr));

    auto root = std::make_shared<Object>();
    root->props["a"] = int64_t{1};
    root->props["b"] = std::shared_ptr<const Array>(arr);
    root->props["s"] = std::string("text");
    return std::shared_ptr<const Object>(root);
}

// 辅助：构造一个仅包含空数组的根对象
// 作用：用于测试空数组的 array_view 边界行为
static std::shared_ptr<const Object>
make_empty_array_root()
{
    auto empty_arr = std::make_shared<Array>();
    auto root = std::make_shared<Object>();
    root->props["e"] = std::shared_ptr<const Array>(empty_arr);
    return std::shared_ptr<const Object>(root);
}

TEST(ViewTest, LookupAndGet)
{
    View v(make_sample());

    auto a = v.subobj("a");
    ASSERT_TRUE(a.has_value());
    auto ai = a->template get<int64_t>();
    ASSERT_TRUE(ai.has_value());
    EXPECT_EQ(*ai, 1);

    auto b2x = v.lookup("b[2].x");
    ASSERT_TRUE(b2x.has_value());
    auto sx = b2x->template get<std::string>();
    ASSERT_TRUE(sx.has_value());
    EXPECT_EQ(*sx, "ok");
}

TEST(ViewTest, ArrayView)
{
    View v(make_sample());
    auto b = v.subobj("b");
    ASSERT_TRUE(b.has_value());
    auto av = b->array_view();
    ASSERT_TRUE(av.has_value());
    ASSERT_EQ(av->size(), 3u);
}

TEST(ViewTest, HasAndEntries)
{
    View v(make_sample());
    EXPECT_TRUE(v.has("a"));
    EXPECT_TRUE(v.has("b"));
    EXPECT_TRUE(v.has("s"));

    auto e = v.object_view();
    ASSERT_TRUE(e.has_value());

    auto ent = v.entries();
    ASSERT_TRUE(ent.has_value());
    // 只检查键是否都在（map 无序）
    std::set<std::string> keys;
    for (auto & kv : *ent)
        keys.insert(std::string(kv.key));
    EXPECT_TRUE(keys.count("a"));
    EXPECT_TRUE(keys.count("b"));
    EXPECT_TRUE(keys.count("s"));
}

// 边界用例：缺失键与类型不匹配
TEST(ViewTest, MissingKeyAndTypeMismatch)
{
    View v(make_sample());

    // 缺失键
    auto miss = v.subobj("not_exist");
    ASSERT_FALSE(miss.has_value());
    EXPECT_EQ(miss.error().code, Errc::MissingKey);

    // 类型不匹配：对非字符串执行 get<std::string>
    auto a = v.subobj("a");
    ASSERT_TRUE(a.has_value());
    auto s = a->template get<std::string>();
    ASSERT_FALSE(s.has_value());
    EXPECT_EQ(s.error().code, Errc::TypeMismatch);
}

// 边界用例：非法路径解析（缺失 ']' 或 非数字索引）
TEST(ViewTest, LookupInvalidPath)
{
    View v(make_sample());

    auto bad1 = v.lookup("b[1"); // 缺失 ']'
    ASSERT_FALSE(bad1.has_value());
    EXPECT_EQ(bad1.error().code, Errc::InvalidPath);

    auto bad2 = v.lookup("b[x]"); // 非数字索引
    ASSERT_FALSE(bad2.has_value());
    EXPECT_EQ(bad2.error().code, Errc::InvalidPath);
}

// 边界用例：数组越界访问
TEST(ViewTest, ArrayOutOfRange)
{
    View v(make_sample());
    auto b = v.subobj("b");
    ASSERT_TRUE(b.has_value());

    auto out = b->at(5);
    ASSERT_FALSE(out.has_value());
    EXPECT_EQ(out.error().code, Errc::OutOfRange);
}

// 边界用例：对非数组调用 array_view
TEST(ViewTest, ArrayViewOnNonArray)
{
    View v(make_sample());
    auto a = v.subobj("a");
    ASSERT_TRUE(a.has_value());

    auto av = a->array_view();
    ASSERT_FALSE(av.has_value());
    EXPECT_EQ(av.error().code, Errc::TypeMismatch);
}

// 边界用例：对非对象调用 object_view
TEST(ViewTest, ObjectViewOnNonObject)
{
    View v(make_sample());
    auto a = v.subobj("a");
    ASSERT_TRUE(a.has_value());

    auto ov = a->object_view();
    ASSERT_FALSE(ov.has_value());
    EXPECT_EQ(ov.error().code, Errc::TypeMismatch);
}

// 边界用例：空数组的 array_view 应返回 size 为 0
TEST(ViewTest, EmptyArrayView)
{
    View v(make_empty_array_root());
    auto e = v.subobj("e");
    ASSERT_TRUE(e.has_value());

    auto av = e->array_view();
    ASSERT_TRUE(av.has_value());
    EXPECT_EQ(av->size(), 0u);
}

// 边界用例：get_int64 对 double 的非整数值与溢出值
TEST(ViewTest, GetInt64FromDoubleInvalidCases)
{
    Builder b;
    b.set("d1", double{3.5}).set("d2", std::numeric_limits<double>::max());

    auto fr = b.freeze();
    ASSERT_TRUE(fr.has_value());
    View v(*fr);

    auto r1 = v.lookup("d1");
    ASSERT_TRUE(r1.has_value());
    auto i1 = r1->get_int64();
    ASSERT_FALSE(i1.has_value());
    EXPECT_EQ(i1.error().code, Errc::TypeMismatch);

    auto r2 = v.lookup("d2");
    ASSERT_TRUE(r2.has_value());
    auto i2 = r2->get_int64();
    ASSERT_FALSE(i2.has_value());
    EXPECT_EQ(i2.error().code, Errc::TypeMismatch);
}

// 边界用例：无效根视图状态（以空根构造）
TEST(ViewTest, InvalidRootViewState)
{
    View v(std::shared_ptr<const Object>{});

    auto r = v.lookup("a");
    ASSERT_FALSE(r.has_value());
    EXPECT_EQ(r.error().code, Errc::Other);
}

// 边界用例：对非对象调用 entries
TEST(ViewTest, EntriesOnNonObject)
{
    View v(make_sample());
    auto a = v.subobj("a");
    ASSERT_TRUE(a.has_value());

    auto es = a->entries();
    ASSERT_FALSE(es.has_value());
    EXPECT_EQ(es.error().code, Errc::TypeMismatch);
}